﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Urbania.World;

namespace Urbania.Utilities
{
    public class Camera
    {
        protected World.World _world;
        protected Vector2 position;
        protected Vector2 screen;
        public Vector2 ScreenZoomed { get; private set; }
        protected Matrix transform;
        protected float zoom;
        protected int mouseWheelVal;

        // Position at the center of the viewport
        public Vector2 Position { get { return this.position; } }

        // Zoom scalar, contained within [ ZoomMin , ZoomMax ]
        public float Zoom { get { return this.zoom; } }

        // Size of the scrolling margin
        public const int XScrollBand = 50;
        public const int YScrollBand = 50;

        public const float MovementSpeed = 2.8f;

        public const float ZoomIncrement = 0.1f;
        public const float ZoomMin = 0.5f;
        public const float ZoomDefault = 1.0f;
        public const float ZoomMax = 3.0f;

        public Matrix Transform
        {
            get
            {
                this.transform =
                    Matrix.CreateTranslation(new Vector3(-this.position.X, -this.position.Y, 0))
                    *
                    Matrix.CreateScale(zoom)
                    *
                    Matrix.CreateTranslation(new Vector3(+screen.X / 2.0f, +screen.Y / 2.0f, 0))
                    ;
                return this.transform;
            }
        }

        public Camera(Vector2 position, World.World world, GraphicsDevice device)
        {
            this._world = world;
            this.position = position;
            this.screen = new Vector2(device.Viewport.Width, device.Viewport.Height);
            this.ScreenZoomed = new Vector2(device.Viewport.Width, device.Viewport.Height);
            this.zoom = ZoomDefault;
        }

        public SamplerState GetSamplerForZoomLevel()
        {
            SamplerState samplerState;
            if (Zoom <= 2.0f)
            {
                samplerState = SamplerState.LinearClamp;
            }
            else
            {
                samplerState = SamplerState.PointClamp;
            }
            return samplerState;
        }

        public Rectangle GetViewportBounds()
        {
            int xpos = (int)(position.X - ScreenZoomed.X / 2.0f);
            int ypos = (int)(position.Y - ScreenZoomed.Y / 2.0f);
            int width = (int)(ScreenZoomed.X);
            int height = (int)(ScreenZoomed.Y);
            return new Rectangle(xpos, ypos, width, height);
        }

        public void Update()
        {
            MouseState mouse = Utilities.MouseWrap.MouseState;
            KeyboardState keys = Keyboard.GetState();

            bool mouseXInWindow = mouse.X >= 0 && mouse.X < screen.X;
            bool mouseYInWindow = mouse.Y >= 0 && mouse.Y < screen.Y;
            if (!mouseXInWindow || !mouseYInWindow) // If outside viewport, don't modify camerapos
            {
                return;
            }

            float boundsRight = _world.WidthRender - ScreenZoomed.X / 2.0f - Tile.TranslateWidth / 4.0f;
            float boundsLeft = ScreenZoomed.X / 2.0f - Tile.TranslateWidth / 2.0f;
            float boundsDown = _world.HeightRender - ScreenZoomed.Y / 2.0f;
            float boundsUp = ScreenZoomed.Y / 2.0f - Tile.Height / 2.0f;

            bool moveRight = (position.X <= boundsRight)
                && (mouse.X >= this.screen.X - XScrollBand
                || keys.IsKeyDown(Keys.D)
                || keys.IsKeyDown(Keys.Right));
            bool moveLeft = (position.X >= boundsLeft)
                && (mouse.X <= XScrollBand
                || keys.IsKeyDown(Keys.A)
                || keys.IsKeyDown(Keys.Left));
            bool moveDown = (position.Y <= boundsDown)
                && (mouse.Y >= this.screen.Y - YScrollBand
                || keys.IsKeyDown(Keys.S)
                || keys.IsKeyDown(Keys.Down));
            bool moveUp = (position.Y >= boundsUp)
                && (mouse.Y <= YScrollBand
                || keys.IsKeyDown(Keys.W)
                || keys.IsKeyDown(Keys.Up));
            if (moveRight)
            {
                this.position.X += MovementSpeed * (1 / zoom);
            }
            else if (moveLeft)
            {
                this.position.X -= MovementSpeed * (1 / zoom);
            }

            if (moveDown)
            {
                this.position.Y += MovementSpeed * (1 / zoom);
            }
            else if (moveUp)
            {
                this.position.Y -= MovementSpeed * (1 / zoom);
            }

            if (keys.IsKeyDown(Keys.H))
            {
                this.position = _world.HomePositionWorld;
            }

            if (mouse.ScrollWheelValue > this.mouseWheelVal)
            {
                this.mouseWheelVal = mouse.ScrollWheelValue;
                if (zoom <= ZoomMax)
                {
                    zoom += ZoomIncrement * this.zoom; // Scaled by zoom for linear controls
                    //position.X = MouseWrap.PositionWorld.X;
                    //position.Y = MouseWrap.PositionWorld.Y;
                }
            }
            else if (mouse.ScrollWheelValue < this.mouseWheelVal)
            {
                this.mouseWheelVal = mouse.ScrollWheelValue;
                if (zoom >= ZoomMin)
                {
                    zoom -= ZoomIncrement * this.zoom; // Scaled by zoom for linear controls
                    //position.X = MouseWrap.PositionWorld.X;
                    //position.Y = MouseWrap.PositionWorld.Y;
                }
            }

            if (MouseWrap.isDown(MouseWrap.MiddleClick))
            {
                zoom = ZoomDefault;
            }

            ScreenZoomed = screen * (1.0f / zoom);
            
        }
    }
}
